package von.seiji.cn;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.beust.jcommander.internal.Lists;
import org.opencv.core.Point;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: Amasawa Seiji
 * @Since: 2021/1/28 6:32
 * @Description: 暂无描述
 */

public class PointUtil {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        try {
        } catch (Exception e) {
        }
    }

    //返回矩形中心点坐标
    public static Point calCenter(double left, double top, double right, double bottom) {
        return new Point((left + right) / 2, (top + bottom) / 2);
    }

    //计算两点之间的距离
    public static double calDistance(Point arg1, Point arg2) {//得到与另一点之间的距离，平面几何中。点与点的距离为：开根((x1-x2)平方+(y1-y2)平方)
        return Math.sqrt(Math.pow(arg1.x - arg2.x, 2) + Math.pow(arg1.y - arg2.y, 2));
    }

    /**
     * 判断p是否在abcd组成的四边形内(凸四边形)
     *
     * @param a
     * @param b
     * @param c
     * @param d
     * @param p
     * @return 如果p在四边形内返回true, 否则返回false.
     */
    public static boolean pInQuadrangle(Point a, Point b, Point c, Point d,
                                        Point p) {
        double dTriangle = triangleArea(a, b, p) + triangleArea(b, c, p)
                + triangleArea(c, d, p) + triangleArea(d, a, p);
        double dQuadrangle = triangleArea(a, b, c) + triangleArea(c, d, a);
        return dTriangle == dQuadrangle;
    }

    // 返回三个点组成三角形的面积
    private static double triangleArea(Point a, Point b, Point c) {
        double result = Math.abs((a.x * b.y + b.x * c.y + c.x * a.y - b.x * a.y
                - c.x * b.y - a.x * c.y) / 2.0D);
        return result;
    }

    // 返回三个点组成三角形的面积
    private static double triangleArea1(Point a, Point b, Point c) {

        Point ab = new Point(a.x - b.x, a.y - b.y);
        Point ac = new Point(a.x - c.x, a.y - c.y);
        double result = Math.abs((ab.x * ac.y - ac.x * ab.y) / 2.0D);
        return result;
    }

    //如果p在四边形内返回true, 否则返回false.
    public boolean pInQuadrangle(Point t, Point... points) {
        double[] _vector = new double[4];
        for (int i = 0; i < points.length; i++) {
            Point vector1 = new Point(points[i].x - t.x, points[i].y - t.y);
            Point vector2 = new Point(points[(i + 1) % 4].x - t.x, points[(i + 1) % 4].y - t.y);
            _vector[i] = vector1.x * vector2.y - vector1.y * vector2.x;
        }
        return (_vector[0] >= 0 && _vector[1] >= 0 && _vector[2] >= 0 && _vector[3] >= 0) ||
                (_vector[0] <= 0 && _vector[1] <= 0 && _vector[2] <= 0 && _vector[3] <= 0);
    }

    public static Point[] pIn(Point t, JSONArray points) {
        List<Point> collect = points.stream().map(item -> new Point(((JSONObject) item)
                .getIntValue("x"), ((JSONObject) item).getIntValue("y"))).collect(Collectors.toList());
        return collect.toArray(new Point[0]);
    }

   /* public static Point[] pIn(Point t, JSONArray points) {
        List<Point> collect = points.stream().map(item -> {
            JSONArray item1 = (JSONArray) item;
            return new Point(item1.getIntValue(0), item1.getIntValue(1));
        }).collect(Collectors.toList());
        return collect.toArray(new Point[0]);
    }*/


    //如果p在四边形内返回true, 否则返回false.
    public static boolean polygonJudge(Point target, Point... points) {
        MatOfPoint2f mat = new MatOfPoint2f(points);
        /*
         * 执行轮廓点测试。 该函数确定该点是在轮廓内部，外部还是在边缘上（或与顶点重合）。 它分别返回正（内部），负（外部）或零（边缘）值。
         * 当measureDist = false时，返回值分别为+ 1，-1和0。 否则，返回值是该点与最近的轮廓边之间的有符号距离。
         * 参见下面的函数示例输出，其中针对轮廓测试每个图像像素：
         * @param contour 输入轮廓。
         * @param pt 测试点。
         * @param measureDist如果为true，则该函数估计从点到最近的轮廓边的有符号距离。 否则，该功能仅检查该点是否在轮廓内。
         */
        return Imgproc.pointPolygonTest(mat, target, false) >= 0;
    }

    public static boolean polygonJudge(Point target, List<Point> points) {
        return polygonJudge(target, points.toArray(new Point[0]));
    }

    //如果p在四边形内返回true, 否则返回false.
    public static boolean polygonJudge(Point target, int left, int top, int right, int bottom) {
        return polygonJudge(target, new Point(left, top), new Point(right, top), new Point(right, bottom), new Point(left, bottom));
    }

    //如果四边形在轮廓内部返回true, 否则返回false.
    public static boolean polygonInContour(int left, int top, int right, int bottom, Point... points) {
        List<Point> points1 = Lists.newArrayList(new Point(left, top), new Point(right, top), new Point(right, bottom), new Point(left, bottom));
        return points1.stream().allMatch(item -> polygonJudge(item, points));
    }



  /*  public static BufferedImage convert2Image(Mat mat) {
        int width = mat.cols();
        int height = mat.rows();
        int dims = mat.channels();
        int[] pixels = new int[width * height];
        byte[] rgbdata = new byte[width * height * dims];
        mat.get(0, 0, rgbdata);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int index = 0;
        int r = 0, g = 0, b = 0;
        for (int row = 0; row < height; row++) {
            for (int col = 0; col < width; col++) {
                if (dims == 3) {
                    index = row * width * dims + col * dims;
                    b = rgbdata[index] & 0xff;
                    g = rgbdata[index + 1] & 0xff;
                    r = rgbdata[index + 2] & 0xff;
                    pixels[row * width + col] = ((255 & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | b & 0xff;
                }
                if (dims == 1) {
                    index = row * width + col;
                    b = rgbdata[index] & 0xff;
                    pixels[row * width + col] = ((255 & 0xff) << 24) | ((b & 0xff) << 16) | ((b & 0xff) << 8) | b & 0xff;
                }
            }
        }
        setRGB(image, 0, 0, width, height, pixels);
        return image;
    }*/

    /**
     * A convenience method for setting ARGB pixels in an image. This tries to avoid the performance
     * penalty of BufferedImage.setRGB unmanaging the image.
     */
    /*public static void setRGB(BufferedImage image, int x, int y, int width, int height, int[] pixels) {
        int type = image.getType();
        if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB)
            image.getRaster().setDataElements(x, y, width, height, pixels);
        else
            image.setRGB(x, y, width, height, pixels, 0, width);
    }*/
    public static Mat byte2Mat(byte[] bytes) {
        return Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_UNCHANGED);
    }

    public static final String JPG = ".jpg";
    public static final String PNG = ".png";

    public static byte[] mat2Byte(Mat mat, String ext) {
        MatOfByte aByte = new MatOfByte();
        Imgcodecs.imencode(ext, mat, aByte);
        return aByte.toArray();
    }

    public static BufferedImage mat2BufferedImage(Mat mat, String ext) {
        MatOfByte aByte = new MatOfByte();
        Imgcodecs.imencode(ext, mat, aByte);
        try {
            return ImageIO.read(new ByteArrayInputStream(aByte.toArray()));
        } catch (IOException e) {
        }
        return null;
    }

    public static BufferedImage convert2Image(Mat matrix) {
        int type = BufferedImage.TYPE_BYTE_GRAY;
        if (matrix.channels() > 1) {
            type = BufferedImage.TYPE_3BYTE_BGR;
        }
        int bufferSize = matrix.channels() * matrix.cols() * matrix.rows();
        byte[] buffer = new byte[bufferSize];
        matrix.get(0, 0, buffer);
        // 获取所有的像素点
        BufferedImage image = new BufferedImage(matrix.cols(), matrix.rows(), type);
        final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
        return image;
    }



}
